# coding=utf-8
import asyncio
import base64
import json
import re
import time
from datetime import datetime, timedelta
from io import BytesIO
import nest_asyncio
nest_asyncio.apply()

import PIL
import aiohttp
import ddddocr
import json5
import requests
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.ui import WebDriverWait

import SeleniumUtils
from SeleniumUtils import SeleniumUtils

# username = "2020412004"  # 账号
# password = "Ctbu@751913@"  # 密码

logUrl = "https://ic.ctbu.edu.cn"  # 登录网址


# targetUrl = "http://ic.ctbu.edu.cn/ClientWeb/pro/ajax/reserve.aspx?"  # get请求数据的网址
# data = {  # get请求提交的数据
#     "dev_id": "100456026",  # 座位号
#     "type": "dev",  # 必要参数
#     "start": "2022-3-20 14:00",
#     "end": "2022-3-20 22:00",
#     "act": "set_resv",  # 必要参数
# }


class SchoolUtils:
    def __init__(self):
        self.cookie = None
        self.ocr = ddddocr.DdddOcr()
        self.bro = self.__browser_initial()
        self.header = {  # get/post请求的表头
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 "
                          "Safari/537.36 Edg/110.0.1587.63",
            "Cookie": "ic-cookie=ef80c2f9-c98d-4670-85a5-0ea6b6496634",  # 需要进行修改,改为真实cookie
            # post 传送数据json
            "Content-Type": "application/json;charset=UTF-8",
        }

        self.captcha = asyncio.Queue(maxsize=1)  # 验证码队列
        with open('config.json5', 'r', encoding="utf8") as f:
            self.config = json5.load(f)
        self.username = self.config["username"]
        self.password = self.config["password"]
        # 将日期加1天，得到明天的日期
        tomorrow = datetime.now() + timedelta(days=1)
        self.start_time = datetime.combine(tomorrow, datetime.strptime(self.config["begin_time"], '%H:%M').time())
        self.end_time = datetime.combine(tomorrow, datetime.strptime(self.config["end_time"], '%H:%M').time())

        self.user_info_dict = None  # 存储用户信息字典
        self.seats_info_list = None  # 存储座位信息字典
        self.cookie = self.auto_login(self.bro)
        # 修改header中的cookie
        self.header["Cookie"] = f"ic-cookie={self.cookie.get('ic-cookie')}"
        # 以下函数需要拥有cookie才行
        self.__get_user_info()
        self.__get_seats_info()

    @staticmethod
    def __extract_numbers(string):
        numbers = re.findall(r"\d+", string)
        return numbers

    @staticmethod
    #  验证码优化
    def __transparent_back(img):
        img = img.convert("RGBA")
        L, H = img.size
        color_0 = img.getpixel((0, 0))
        color_2 = img.getpixel((5, 5))
        color_list = []
        for i in range(25):
            color_list.append(img.getpixel((i, i)))
        for h in range(H):
            for l in range(L):
                dot = (l, h)
                color_1 = img.getpixel(dot)
                if color_1 in color_list:
                    color_1 = color_1[:-1] + (0,)
                    img.putpixel(dot, color_1)
        return img.crop((20, 0, 75, 25))  # 只返回关键部分

    def auto_login(self, bro):
        # 得到cookie
        bro.get(logUrl)  # 登录
        # 设置等待时间
        wait = WebDriverWait(bro, 20)
        # 等待页面标题出现
        wait.until(expected_conditions.title_contains("统一身份认证平台"), "等待超时")

        """填充密码"""
        usernameInput = bro.find_element("id", value="username")
        usernameInput.send_keys(self.username)
        passwordInput = bro.find_element(by=By.ID, value="ppassword")
        passwordInput.send_keys(self.password)
        """获取图片并优化"""
        captcha_img = bro.find_element(by=By.ID, value="yzmPic")

        # captcha_img = requests.get(imgUrl).content
        img_binary_data = base64.b64decode(captcha_img.screenshot_as_base64)  # 转化为二进制
        binary_img = BytesIO(img_binary_data)
        captcha_img = PIL.Image.open(binary_img)  # 得到原图片

        # img = transparent_back(captcha_img)  # 得到优化图片
        img = captcha_img  # 得到优化图片

        """ocr识别验证码并填充"""
        bytesIO = BytesIO()
        img.save(bytesIO, format="PNG")
        res = self.ocr.classification(bytesIO.getvalue())
        result = ""
        for i in res:
            result = result + str(i)
        result = SchoolUtils.__extract_numbers(result)
        captcha_input = bro.find_element(by=By.ID, value="authcode")
        captcha_input.send_keys(result)
        """点击登录"""
        button = bro.find_element(by=By.ID, value="dl")  # 定位搜索按钮
        button.click()  # 执行单击操作
        """获取Cookie"""
        time.sleep(3)
        cookies = bro.get_cookies()  # 类型是列表
        cookies_dict = {}
        for cookie in cookies:
            cookies_dict[cookie['name']] = cookie['value']
        return cookies_dict

    @staticmethod
    def __browser_initial():
        """
        进行浏览器初始化
        """
        options = webdriver.ChromeOptions()
        # options.add_argument("--headless")  # 设置chrome为headless无界面模式
        options.add_argument("--disable-gpu")
        bro = SeleniumUtils.get_chrome_driver(options)
        return bro

    def __get_user_info(self):
        print("正在获取用户信息")
        url_get_user_info = "https://ic.ctbu.edu.cn/ic-web/auth/userInfo"
        response = json.loads(requests.get(url_get_user_info, cookies=self.cookie, headers=self.header).text)["data"]
        print("当前获取到用户为", response["trueName"])
        self.user_info_dict = response

    def __get_seats_info(self):
        # 获取所有的座位信息,需要传入参数
        print("正在获取座位信息")
        tomorrow = (datetime.now() + timedelta(days=1)).strftime('%Y%m%d')
        url_seats_5 = f"https://ic.ctbu.edu.cn/ic-web/reserve?roomIds=100455854&resvDates={tomorrow}&sysKind=8"
        url_seats_6 = f"https://ic.ctbu.edu.cn/ic-web/reserve?roomIds=100455856&resvDates={tomorrow}&sysKind=8"
        self.seats_info_list = json.loads(requests.get(url_seats_5, cookies=self.cookie).text)["data"] + \
                               json.loads(requests.get(url_seats_6, cookies=self.cookie).text)["data"]

    @staticmethod
    def __is_overlap(start_1, end_1, start_2, end_2):
        # 判断两个时间段是否有重叠
        if start_1 < end_2 and end_1 > start_2:
            return True
        elif start_2 < end_1 and end_2 > start_1:
            return True
        elif start_1 < start_2 and start_2 > end_2:
            return True
        else:
            return False

    def __get_available_seat_ids(self):
        """
        获取所有能够使用的座位的id
        :return:
        """
        available_seat_ids = []

        for seat in self.seats_info_list:
            if seat["devName"] not in self.config["expect_seats"]:
                # 跳过不需要的位置
                continue
            is_available = True
            # 获取座位的可用时间,如果发生冲突,跳过
            for item in seat["resvInfo"]:
                res_start_time = datetime.fromtimestamp(item["startTime"] / 1000)
                res_end_time = datetime.fromtimestamp(item["endTime"] / 1000)
                if self.__is_overlap(self.start_time, self.end_time, res_start_time, res_end_time):
                    # 有重叠,返回False
                    is_available = False
                    break
            if not is_available:
                continue
            available_seat_ids.append(seat["devId"])

        return available_seat_ids

    async def __send_requests(self, seat_id, session):
        """
        需要发起的请求,以及所需要的参数
        :return:
        """
        reserve_data = {
            "sysKind": 8,
            "appAccNo": self.user_info_dict["accNo"],  # 个人专属 可改
            "memberKind": 1,
            "resvMember": [
                self.user_info_dict["accNo"]  # 个人专属 可改
            ],
            "resvBeginTime": self.start_time.strftime("%Y-%m-%d %H:%M:%S"),  # 开始时间 2023-10-15 10:58:00
            "resvEndTime": self.end_time.strftime("%Y-%m-%d %H:%M:%S"),  # 结束时间
            "testName": "",
            "captcha": "",
            "resvProperty": 0,
            "resvDev": [
                seat_id  # 需要座位id
            ],
            "memo": ""
        }  # 负载模板,在请求时进行修改
        url_reserve = "https://ic.ctbu.edu.cn/ic-web/reserve"
        # print("开始发送请求")
        while True:
            captcha = await self.captcha.get()
            print("获取到验证码", captcha)
            reserve_data["captcha"] = captcha
            self.captcha.task_done()
            async with session.post(url_reserve, data=json.dumps(reserve_data)) as response:
                text = await response.text()
                message = json.loads(text)["message"]
                print("接口返回信息", message)
                if "成功" in message or "用户在当前时段有预约" in message:
                    return message

            # response = requests.post(url_reserve, cookies=self.cookie, data=json.dumps(reserve_data),
            #                          headers=self.header)

            # message = json.loads(response.text)["message"]

            # time.sleep(0.05)

    async def __get_captcha(self, session):
        """
        异步获取
        :return:
        """
        # 获取验证码
        # content = requests.get(
        #                        headers=self.header, cookies=self.cookie).content
        # image = Image.open(BytesIO(content))
        # res = "".join(self.ocr.classification(image))
        # print(f"获取到验证码为{res}")
        # reserve_data["captcha"] = res
        # 预订url
        # response = await session.post(url_reserve, data=json.dumps(reserve_data))
        url_captcha = f"https://ic.ctbu.edu.cn/ic-web/captcha?id={int(datetime.now().timestamp() * 1000)}"
        res = None
        while True:
            await self.captcha.put(res)
            async with session.get(url_captcha) as response:
                content = await response.read()
                image = Image.open(BytesIO(content))
                res = "".join(self.ocr.classification(image))
                print(f"接口获取验证码为{res}")

    async def __do_reserve(self):
        """
        执行预订操作,需要修改appAccNo
        :return:
        """
        # 获取能够使用的座位编号
        available_seats_ids = self.__get_available_seat_ids()
        print(f"当前选择中可使用的座位有{len(available_seats_ids)}个")
        async with aiohttp.ClientSession(cookies=self.cookie, headers=self.header) as session:
            tasks = [asyncio.ensure_future(self.__send_requests(seat_id, session)) for seat_id in available_seats_ids]
            tasks_captcha = [asyncio.ensure_future(self.__get_captcha(session)) for _ in range(1)]
            tasks = tasks + tasks_captcha
            future = asyncio.gather(*tasks)
            asyncio.get_running_loop().run_until_complete(future)
            # await asyncio.gather()
            # asyncio.get_event_loop().run_until_complete(future)
            # # 等待任意一个任务完成
            # done, pending = await asyncio.wait(tasks, return_when=asyncio.ALL_COMPLETED)
            # print(done,"已完成")
            #
            # 取消所有未完成的任务
            # for task in pending:
            #     task.cancel()
            #
            # # 等待所有任务完成
            # await asyncio.gather(*done, return_exceptions=True)
            #
            # 处理已完成的任务结果
            # for task in done:
            #     try:
            #         result = task.result()
            #         print(result)
            #     except Exception as e:
            #         # print(f'Error: {e}')
            #         pass
            print("抢座位完成,正在退出")

    def __check_grab_time(self):
        """
        判断当前时间是否支持开始抢座位
        :return:
        """
        now = datetime.now()
        target_time = now.replace(hour=22, minute=25, second=0, microsecond=0)

        if now >= target_time:
            print("当前时间已经超过当天的22:25")
            time_diff = now - target_time
            print("超过时间：", time_diff)
            print("超过小时数：", time_diff.seconds // 3600)
            print("超过分钟数：", (time_diff.seconds // 60) % 60)
            return True
        else:
            print("当前时间未超过当天的22:25")
            return False

    def grab_seat(self):

        sleep_time = 0
        while not self.__check_grab_time():
            # 判断当前时间是否能够抢座位
            print("未到抢座时间,睡眠5秒")
            sleep_time = sleep_time + 1
            time.sleep(5)
            if sleep_time % 10 == 0:
                sleep_time = 0
                # self.bro.refresh()
                self.__get_user_info()
                print(f"刷新cookie,cookie为{self.header['Cookie']}")

        # url_seat_menu = "https://ic.ctbu.edu.cn/ic-web/seatMenu"  # 获取座位菜单信息url
        asyncio.run(self.__do_reserve())

    def close(self):
        """
        关闭浏览器,释放资源
        :return:
        """
        self.bro.close()
        pass


if __name__ == "__main__":
    school_handle = SchoolUtils()
    school_handle.grab_seat()
    school_handle.close()
